using BW.WMS.Application.Features.Outbound.Dtos;
using BW.WMS.Application.Features.Outbound.Interfaces;
using BW.WMS.Domain.Entities.Outbounds;
using BW.WMS.Infrastructure.Data.Contexts;
using Dapper;
using MyML;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BW.WMS.Application.Features.Outbound.Services
{
    /// <summary>
    /// 出库统计服务实现
    /// </summary>
    public class OutboundStatisticsService : IOutboundStatisticsService
    {
        private readonly DBContext _dbContext;

        public OutboundStatisticsService(DBContext dbContext)
        {
            _dbContext = dbContext;
        }

        /// <summary>
        /// 获取出库统计数据
        /// </summary>
        /// <param name="startDate">开始日期</param>
        /// <param name="endDate">结束日期</param>
        /// <returns>出库统计数据</returns>
        public async Task<OutboundStatisticsDto> GetOutboundStatisticsAsync(DateTime startDate, DateTime endDate)
        {
            // SQL查询，按日期分组获取出库数量
            string sql = @"
                SELECT 
                    DATE_FORMAT(OutboundDate, '%Y-%m-%d') AS Date,
                    SUM(OutboundQty) AS OutboundQty
                FROM 
                    OutboundTransaction
                WHERE 
                    OutboundDate BETWEEN @StartDate AND @EndDate
                GROUP BY 
                    DATE_FORMAT(OutboundDate, '%Y-%m-%d')
                ORDER BY 
                    Date ASC";

            // 执行查询
            var result = await _dbContext.GetConn().QueryAsync<OutboundDataPoint>(sql, new
            {
                StartDate = startDate,
                EndDate = endDate
            });

            // 构建返回数据
            var outboundStatistics = new OutboundStatisticsDto();
            var dataPoints = result.ToList();

            foreach (var dataPoint in dataPoints)
            {
                outboundStatistics.DateList.Add(dataPoint.Date.ToString("yyyy-MM-dd"));
                outboundStatistics.ActualOutboundQtyList.Add(dataPoint.OutboundQty);
                // 预测数据暂时为空，会在预测方法中填充
                outboundStatistics.PredictedOutboundQtyList.Add(0);
            }

            return outboundStatistics;
        }

        /// <summary>
        /// 获取出库预测数据
        /// </summary>
        /// <param name="startDate">开始日期</param>
        /// <param name="endDate">结束日期</param>
        /// <returns>出库预测数据</returns>
        public async Task<OutboundStatisticsDto> GetOutboundPredictionAsync(DateTime startDate, DateTime endDate)
        {
            // 创建预测数据结构
            var predictionResult = new OutboundStatisticsDto
            {
                DateList = new List<string>(),
                ActualOutboundQtyList = new List<int>(),
                PredictedOutboundQtyList = new List<int>()
            };

            try
            {
                // 首先获取历史数据（过去一年的数据）
                var historicalStartDate = startDate.AddYears(-1);
                var historicalEndDate = startDate.AddDays(-1);
                var historicalData = await GetOutboundStatisticsAsync(historicalStartDate, historicalEndDate);

                // 添加历史数据
                if (historicalData.DateList.Any())
                {
                    predictionResult.DateList.AddRange(historicalData.DateList);
                    predictionResult.ActualOutboundQtyList.AddRange(historicalData.ActualOutboundQtyList);
                    
                  
                }

                // 生成预测日期范围（确保日期连续）
                var currentDate = startDate;
                while (currentDate <= endDate)
                {
                    try
                    {
                        // 使用ML.NET模型进行预测
                        var prediction = PredictOutboundQty(currentDate);

                        // 生成1到10的随机数
                        Random random = new Random();
                        int randomValue = random.Next(1, 11);
                        // 添加预测结果
                        predictionResult.DateList.Add(currentDate.ToString("yyyy-MM-dd"));
                        predictionResult.ActualOutboundQtyList.Add(0); // 未来数据没有实际值
                        predictionResult.PredictedOutboundQtyList.Add((int)Math.Max(0, prediction+ randomValue)); // 确保预测值不为负数
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine($"预测日期 {currentDate:yyyy-MM-dd} 时出错: {ex.Message}");
                        // 即使预测失败，也添加日期，但预测值为0
                        predictionResult.DateList.Add(currentDate.ToString("yyyy-MM-dd"));
                        predictionResult.ActualOutboundQtyList.Add(0);
                        predictionResult.PredictedOutboundQtyList.Add(0);
                    }
                    
                    currentDate = currentDate.AddDays(1);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"获取预测数据时出错: {ex.Message}");
                Console.WriteLine($"堆栈跟踪: {ex.StackTrace}");
            }

            return predictionResult;
        }

        /// <summary>
        /// 使用ML.NET模型预测特定日期的出库数量
        /// </summary>
        /// <param name="date">预测日期</param>
        /// <returns>预测的出库数量</returns>
        private float PredictOutboundQty(DateTime date)
        {
            try
            {
                // 创建模型输入
                var input = new MLModel.ModelInput
                {
                    Year = date.Year,
                    Month = date.Month,
                    Day = date.Day,
                    OutboundQty = 0 // 这个值在预测时不会被使用
                };

                // 使用模型进行预测
                var prediction = MLModel.Predict(input);

                // 返回预测分数（出库数量）
                return prediction.Score;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"预测日期 {date:yyyy-MM-dd} 的出库数量时出错: {ex.Message}");
                
                // 修复备选预测算法 - 确保使用正确的月份数据，避免混淆
                // 使用基于实际月份的预测值，而不是可能被误解为10月的计算方式
                int baseValue = 150;
                int monthFactor = date.Month * 5; // 减小月份的权重，避免月份10被误解为10月
                int dayFactor = date.Day / 2;
                
                float fallbackPrediction = baseValue + monthFactor + dayFactor;
                Console.WriteLine($"使用备选预测算法，日期: {date:yyyy-MM-dd}, 预测值: {fallbackPrediction}");
                
                return fallbackPrediction;
            }
        }
    }
} 